package core

import (
	"bytes"
	_ "crypto/md5"
	"crypto/sha256"
	"encoding/hex"
	"strconv"
	"strings"
	"time"
)

//Account是一个账户的结构体
//
//# Address 是这个账户的地址，16位十六进制字符串
//
//# Balance 是这个账户的余额，int64

type Account struct {
	Address string
	name    string
	Balance int64
}

type Accounts struct {
	accounts []Account
	// 用一个map将名字和其账户序号一一对应
	account map[string]int
}

//transaction是一个交易的结构体
//
//# From 是该笔交易的发出方，储存的是发出方的十六进制字符串地址
//
//# To 是该笔交易的接收方，储存的是接收方的十六进制字符串地址
//
//# Value 是这比交易的金额

type Transaction struct {
	From  []string
	To    []string
	Value []int64
}

// Block 是一个简易的"区块"结构体，区块由多笔交易构成
//
// # Index 是该区块的序号，在区块链中，区块的序号单调递增，创世区块的序号为0
//
// # TimeStamp 是该区块的时间戳
//
// # PreviousHash 是前一区块的哈希值，正是因为这一字段的存在，各个区块才能组成防篡改的链式结构
//
// # Hash 是当前区块的哈希值，这一字段保证了本区块内的交易不被篡改
//
// Transactions 是当前区块内的交易组成的切片，是该结构体的主要荷载

type Block struct {
	Index        int64
	TimeStamp    int64
	PreviousHash string
	Hash         string
	Transaction  //结构体
}

// GenerateNewBlock 会生成一个新的区块并返回，具体执行流程包括：
//
// (1)生成一个 Block 区块实例，并将该实例的字段初始化
//
// (2)该实例中的 Index 字段和 PreviousHash 的初始化需要借助传参previousBlock的信息
//
// (2)该实例中的 Transactions 字段由传参 transactions 得到
//
// (4)通过 CalculateHash 函数计算该区块的哈希值，并将结果赋给该实例的 Hash 字段
//
// (5)返回该区块实例
//
// 在这个函数里transactions的数组永远都是两个（当前）
func GenerateNewBlock(previousBlock *Block, transactions []string) *Block {
	//生成一个区块的实例
	block := new(Block)
	//初始化内容
	block.Index = previousBlock.Index + 1
	block.TimeStamp = time.Now().Local().Unix()
	block.PreviousHash = previousBlock.Hash
	//block.Transactions = transactions
	//用新的transaction结构体来进行赋值
	//首先先把字符串进行切割，然后进行赋值
	//根据传入交易的数量，进行相应的赋值
	temp := len(transactions)
	for i := 0; i < temp; i++ {
		splitString := strings.Split(transactions[i], " ")
		block.Transaction.From = append(block.Transaction.From, splitString[0])
		block.Transaction.To = append(block.Transaction.To, splitString[1])
		value, _ := strconv.ParseInt(splitString[2], 10, 64)
		block.Transaction.Value = append(block.Transaction.Value, value)
	}
	block.Hash = CalculateHash(block)
	//返回实例
	return block
}

// CalculateHash 计算传入的结构体的哈希值，以字符串形式返回
func CalculateHash(b *Block) string {
	var hashValue string
	//根据传入block的时间和序号和前一块block的内容计算哈希值
	//拓展任务2.1使用merkle tree进行哈希值的放入
	//对于创世区块，用时间，index进行哈希值判定
	//如果没有交易，就根据这个区块链的时间戳，index和上一个哈希值进行制作，如果只有一个交易就对这个交易进行加密，如果有两个交易，把这两个交易的哈希值算出来，然后再组合起来获得根部的哈希值
	if len(b.Transaction.From) == 0 {
		timestamp := []byte(strconv.FormatInt(b.TimeStamp, 10))
		index := []byte(strconv.FormatInt(b.Index, 10))
		headers := bytes.Join([][]byte{timestamp, index, []byte(b.PreviousHash)}, []byte{})
		hash := sha256.Sum256(headers)
		hashValue = hex.EncodeToString(hash[:])
		return hashValue
	} else if b.Transaction.From[0] == "nil" {
		timestamp := []byte(strconv.FormatInt(b.TimeStamp, 10))
		index := []byte(strconv.FormatInt(b.Index, 10))
		headers := bytes.Join([][]byte{timestamp, index}, []byte{})
		hash := sha256.Sum256(headers)
		hashValue = hex.EncodeToString(hash[:])
		return hashValue
	} else if len(b.Transaction.From) == 1 {
		var transactions [1]string
		//将区块链中储存的信息提取成交易信息
		transactions[0] = b.Transaction.From[0] + " gave " + b.Transaction.To[0] + " " + strconv.FormatInt(b.Transaction.Value[0], 10) + " dollars."
		hashLeft := sha256.Sum256([]byte(transactions[0]))
		//转换为字符串，然后再合并算出答案
		hashLeftValue := hex.EncodeToString(hashLeft[:])
		var buffer bytes.Buffer //Buffer是一个实现了读写方法的可变大小的字节缓冲
		buffer.WriteString(hashLeftValue)
		headers := buffer.Bytes()
		hash := sha256.Sum256(headers)
		hashValue = hex.EncodeToString(hash[:])
		return hashValue
	} else {
		var transactions [2]string
		//将区块链中储存的信息提取成交易信息
		for i := 0; i <= 1; i++ {
			transactions[i] = b.Transaction.From[i] + " gave " + b.Transaction.To[i] + " " + strconv.FormatInt(b.Transaction.Value[i], 10) + " dollars."
		}
		hashLeft := sha256.Sum256([]byte(transactions[0]))
		hashRight := sha256.Sum256([]byte(transactions[1]))
		//转换为字符串，然后再合并算出答案
		hashLeftValue := hex.EncodeToString(hashLeft[:])
		hashRightValue := hex.EncodeToString(hashRight[:])
		var buffer bytes.Buffer //Buffer是一个实现了读写方法的可变大小的字节缓冲
		buffer.WriteString(hashLeftValue)
		buffer.WriteString(hashRightValue)
		headers := buffer.Bytes()
		hash := sha256.Sum256(headers)
		hashValue = hex.EncodeToString(hash[:])
		return hashValue
	}
}

// GenerateGenesisBlock 的作用与 GenerateNewBlock 类似，都是生成新区块，但是本函数用于生成特殊的区块“创世区块”，创世区块是区块链中的第一个区块，它的序号（Index字段值）为0
func GenerateGenesisBlock() *Block {
	block := new(Block)
	block.Index = 0
	block.PreviousHash = ""
	block.TimeStamp = time.Now().Unix()
	block.Transaction.From = append(block.Transaction.From, "nil")
	block.Transaction.To = append(block.Transaction.To, "nil")
	block.Transaction.Value = append(block.Transaction.Value, 0)
	block.Hash = CalculateHash(block)
	return block
}
